using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace Sunny.UI;

public class ConcurrentGroupDictionary<TGroup, TKey, TValue>
{
	private readonly ConcurrentDictionary<TGroup, ConcurrentDictionary<TKey, TValue>> Dictionary = new ConcurrentDictionary<TGroup, ConcurrentDictionary<TKey, TValue>>();

	public ConcurrentDictionary<TKey, TValue> this[TGroup group]
	{
		get
		{
			if (!ContainsGroup(group))
			{
				return new ConcurrentDictionary<TKey, TValue>();
			}
			return Dictionary[group];
		}
		set
		{
			if (!ContainsGroup(group))
			{
				TryAdd(group);
			}
			Dictionary[group] = value;
		}
	}

	public List<TGroup> Groups => Dictionary.Keys.ToList();

	public List<TGroup> SortedGroups
	{
		get
		{
			List<TGroup> groups = Groups;
			groups.Sort();
			return groups;
		}
	}

	public int AllCount => ((IEnumerable<TGroup>)Groups).Sum((Func<TGroup, int>)GroupItemsCount);

	public int GroupCount => Dictionary.Count;

	public bool ContainsGroup(TGroup group)
	{
		return Dictionary.ContainsKey(group);
	}

	public bool ContainsKey(TKey key)
	{
		return Groups.Any((TGroup group) => ContainsKey(group, key));
	}

	public bool ContainsKey(TGroup group, TKey key)
	{
		if (ContainsGroup(group))
		{
			return Dictionary[group].ContainsKey(key);
		}
		return false;
	}

	public void TryAdd(TGroup group)
	{
		if (!Dictionary.ContainsKey(group))
		{
			Dictionary.TryAdd(group, new ConcurrentDictionary<TKey, TValue>());
		}
	}

	public void TryAdd(TGroup group, TKey key, TValue value)
	{
		TryAdd(group);
		if (ContainsKey(group, key))
		{
			Dictionary[group][key] = value;
		}
		else
		{
			Dictionary[group].TryAdd(key, value);
		}
	}

	public bool TryUpdate(TGroup group, TKey key, TValue value)
	{
		if (!ContainsGroup(group))
		{
			return false;
		}
		TryAdd(group, key, value);
		return true;
	}

	public void TryRemove(TGroup group, TKey key)
	{
		if (ContainsGroup(group))
		{
			if (ContainsKey(group, key))
			{
				Dictionary[group].TryRemove(key, out var _);
			}
			if (GroupItemsCount(group) == 0)
			{
				Dictionary.TryRemove(group, out var _);
			}
		}
	}

	public void TryRemove(TGroup group)
	{
		ClearGroup(group);
		Dictionary.TryRemove(group, out var _);
	}

	public void Clear()
	{
		foreach (TGroup group in Groups)
		{
			ClearGroup(group);
		}
		Dictionary.Clear();
	}

	public void ClearGroup(TGroup group)
	{
		if (ContainsGroup(group))
		{
			Dictionary[group].Clear();
		}
	}

	public List<TValue> Values(TGroup group)
	{
		if (!ContainsGroup(group))
		{
			return new List<TValue>();
		}
		return Dictionary[group].Values.ToList();
	}

	public List<TKey> Keys(TGroup group)
	{
		if (!ContainsGroup(group))
		{
			return new List<TKey>();
		}
		return Dictionary[group].Keys.ToList();
	}

	public List<TKey> SortedKeys(TGroup group)
	{
		List<TKey> list = Keys(group);
		list.Sort();
		return list;
	}

	public List<TValue> SortedValues(TGroup group)
	{
		List<TValue> list = new List<TValue>();
		if (ContainsGroup(group))
		{
			foreach (TKey item in SortedKeys(group))
			{
				list.Add(Dictionary[group][item]);
			}
		}
		return list;
	}

	public TValue GetValue(TGroup group, TKey key)
	{
		if (!ContainsKey(group, key))
		{
			return default(TValue);
		}
		return Dictionary[group][key];
	}

	public int GroupItemsCount(TGroup group)
	{
		if (!ContainsGroup(group))
		{
			return 0;
		}
		return Dictionary[group].Count;
	}
}
